package de.ubschmidt.capi;

import de.ubschmidt.capi.util.ByteBufferFactory;
import java.nio.ByteBuffer;

/**
 * Abstract super class for all CAPI messages which can be sent by
 * an application, i.e. request and response messages.
 */
public abstract class CapiActiveMessage
        extends CapiMessage {

    /** The application ID. */
    private short _appId;

    /** Command. */
    private Command _command;

    /** A unique message number. */
    private short _messageNumber;

    /**
     * Create a new CAPI message.
     *
     * The message number is automatically set to a unique ID.
     *
     * @param command The command.
     */
    public CapiActiveMessage(final Command command) {
        setCommand(command);
        setMessageNumber(getNextMessageNumber());
    }

    /** {@inheritDoc} */
    public short getAppId() {
        return _appId;
    }

    /**
     * Sets the application ID for this message.
     *
     * @param appId The application ID.
     */
    public void setAppId(final short appId) {
        _appId = appId;
    }

    /** {@inheritDoc} */
    public Command getCommand() {
        return _command;
    }

    /**
     * Set the command.
     *
     * @param command The command.
     */
    protected void setCommand(final Command command) {
        _command = command;
    }

    /** {@inheritDoc} */
    public short getMessageNumber() {
        return _messageNumber;
    }

    /**
     * Set the message number.
     *
     * <p>When creating a new request or response message, a new unique
     * message number is filled in automatically.
     *
     * @param messageNumber The message number.
     */
    public void setMessageNumber(final short messageNumber) {
        _messageNumber = messageNumber;
    }

    /** {@inheritDoc} */
    public ByteBuffer getByteBuffer() {
        ByteBuffer byteBuffer = ByteBufferFactory.allocate();

        // fill in the message header.
        byteBuffer.position(2); // skip length - will be filled in last.
        byteBuffer.putShort(_appId);
        _command.writeTo(byteBuffer);
        byteBuffer.putShort(_messageNumber);

        // fill in the message content.
        serializeTo(byteBuffer);

        // fill in total length.
        int length = byteBuffer.position();
        byteBuffer.limit(length);
        byteBuffer.putShort(0, (short) length);

        byteBuffer.rewind();

        return byteBuffer;
    }

    /**
     * Serialize the message-specific part to the given {@code ByteBuffer}.
     *
     * This method is to be implemented by each CAPI message object.
     *
     * @param byteBuffer The message-specific part is written to this
     *                   {@code ByteBuffer}.
     */
    protected abstract void serializeTo(final ByteBuffer byteBuffer);

    /**
     * Calculate and return the total length of this CAPI message.
     *
     * <p>Internally, the binary representation of the CAPI message is
     * created. Thus, this is an expensive method which should be only used
     * for debugging purposes.
     *
     * @return The total length of this CAPI message;
     */
    public short getTotalLength() {
        return (short) getByteBuffer().limit();
    }

    /**
     * Retrieve a new message number.
     *
     * @return A new message number.
     */
    static synchronized short getNextMessageNumber() {
        return ++_nextMessageNumber;
    }

    /** Holds the current message number. */
    private static short _nextMessageNumber = 0;

}
